package product

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/cachex"
	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/utils/pointy"
	"mall/service/product/model"
	"mall/service/product/rpc/internal/svc"
	"mall/service/product/rpc/internal/utils/errorhandler"
	"mall/service/product/rpc/types/product"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

const (
	LSVSeparator           = "-"
	arrayValueSeparator    = ","
	keyValueSeparator      = ":"
	arrayKeyValueSeparator = ";"
	complexLSVSeparator    = "_-_"
)

type BackendGetProductListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewBackendGetProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendGetProductListLogic {
	return &BackendGetProductListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *BackendGetProductListLogic) BackendGetProductList(in *product.ProductListReq) (*product.ProductListResp, error) {
	// logrus.Info(fmt.Sprintf("ProductListReq: %+v", in))

	if in.Diagnose != nil {
		if *in.Diagnose == 1 {
			return l.backendGetProductListStock(in)
		}
	}

	var orderBys []string
	if len(in.OrderBy) > 0 {
		for _, orderBy := range in.OrderBy {
			order := " ASC"
			if orderBy.Order == "1" {
				order = " DESC"
			}
			if orderBy.Field == "id" {
				orderBys = append(orderBys, model.ProductProductColumns.ID+order)
			}
			if orderBy.Field == "sort" {
				orderBys = append(orderBys, model.ProductProductColumns.SortOrder+order)
			}
			if orderBy.Field == "createdAt" {
				orderBys = append(orderBys, model.ProductProductColumns.ProductDate+order)
			}
		}
	}

	var stati string
	var productType string
	if len(in.FilterBy) > 0 {
		for _, filterBy := range in.FilterBy {
			switch filterBy.Field {
			case "status":
				stati = filterBy.In
			case "type":
				productType = filterBy.In
			}
		}
	}

	var page uint64
	var pageSize uint64
	var restricted bool
	var withCount int
	var preCount int64
	filterMap := make(map[string]map[string][]any)
	resp := &product.ProductListResp{}

	if in.VirtualType != nil {
		filterMap[model.ProductProductColumns.VirtualType] = map[string][]any{
			"equalTo": {*in.VirtualType},
		}
	} else {
		filterMap[model.ProductProductColumns.VirtualType] = map[string][]any{
			"equalTo": {globalkey.ProductTypePhysical},
		}
	}

	if in.Ids != nil && len(*in.Ids) > 0 {
		if vals := modelx.UniqueInterfaces(*in.Ids, 64); len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.ProductProductColumns.ID] = map[string][]any{
				"in": vals,
			}
			restricted = true
			withCount = -1
			preCount = int64(len(vals))
		}
	} else if in.SkuIds != nil && len(*in.SkuIds) > 0 {
		if vals, err := l.preFilterByVariation(in); err != nil {
			return nil, err
		} else if len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.ProductProductColumns.ID] = map[string][]any{
				"in": vals,
			}
			restricted = true
			withCount = -1
			preCount = int64(len(vals))
		}
	} else if in.BrandIds != nil && len(*in.BrandIds) > 0 {
		if vals, err := l.preFilterByBrandRelation(in); err != nil {
			return nil, err
		} else if len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.ProductProductColumns.ID] = map[string][]any{
				"in": vals,
			}
			restricted = true
			withCount = -1
			preCount = int64(len(vals))
		}
	} else if in.PerfumeIds != nil && len(*in.PerfumeIds) > 0 {
		if vals, err := l.preFilterByPerfumeRelation(in); err != nil {
			return nil, err
		} else if len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.ProductProductColumns.ID] = map[string][]any{
				"in": vals,
			}
			restricted = true
			withCount = -1
			preCount = int64(len(vals))
		}
	} else if in.CategoryIds != nil && len(*in.CategoryIds) > 0 {
		if vals, err := l.preFilterByTermRelation(in); err != nil {
			return nil, err
		} else if len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.ProductProductColumns.ID] = map[string][]any{
				"in": vals,
			}
			restricted = true
			withCount = -1
			preCount = int64(len(vals))
		}
	}

	var statusIsSet bool
	var isAll bool
	statusMap := make(map[string][]any)
	switch productType {
	case "1": // 全部
		statusMap["notEqualTo"] = []any{globalkey.PostStatusTrash}
		isAll = true
	case "2": // 待审核
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"equalTo": {globalkey.PostStatusPending},
		}
		statusIsSet = true
	case "3": // 已上架 - 销售中
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"equalTo": {globalkey.PostStatusPublish},
		}
		statusIsSet = true
	case "4": // 已上架 - 定时上架、已售罄
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"in": {
				globalkey.ProductStatusUnavailable,
				globalkey.PostStatusFuture,
			},
		}
		statusIsSet = true
	case "5": // 已下架 = 仓库中
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"equalTo": {globalkey.ProductStatusPrivate},
		}
		statusIsSet = true
	case "6": // 已驳回
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"equalTo": {globalkey.PostStatusRejected},
		}
		statusIsSet = true
	case "7": // 草稿
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"equalTo": {globalkey.PostStatusDraft, globalkey.PostStatusAutoDraft},
		}
		statusIsSet = true
	case "8": // 回收站
		filterMap[model.ProductProductColumns.Status] = map[string][]any{
			"equalTo": {globalkey.PostStatusTrash},
		}
		statusIsSet = true
	}

	if !restricted {
		page = in.Page
		pageSize = in.PageSize
		withCount = 1
	}

	if in.Title != nil && len(strings.TrimSpace(*in.Title)) >= 2 {
		filterMap[model.ProductProductColumns.Title] = map[string][]any{
			"contain": {strings.TrimSpace(*in.Title)},
		}
	}
	if in.Slug != nil && len(strings.TrimSpace(*in.Slug)) >= 2 {
		filterMap[model.ProductProductColumns.Slug] = map[string][]any{
			"contain": {strings.TrimSpace(*in.Slug)},
		}
	}
	if in.Excerpt != nil && len(strings.TrimSpace(*in.Excerpt)) >= 2 {
		filterMap[model.ProductProductColumns.Excerpt] = map[string][]any{
			"contain": {strings.TrimSpace(*in.Excerpt)},
		}
	}
	if in.ProductCode != nil && len(strings.TrimSpace(*in.ProductCode)) >= 2 {
		filterMap[model.ProductProductColumns.ProductCode] = map[string][]any{
			"equalTo": {strings.TrimSpace(*in.ProductCode)},
		}
	}
	if in.CategoryId != nil {
		filterMap[model.ProductProductColumns.CategoryID] = map[string][]any{
			"equalTo": {*in.CategoryId},
		}
	}
	if in.ParentId != nil {
		filterMap[model.ProductProductColumns.ParentID] = map[string][]any{
			"equalTo": {*in.ParentId},
		}
	}

	var startTimeIn []any
	var endTimeIn []any
	if in.StartTime != nil && len(strings.TrimSpace(*in.StartTime)) > 0 {
		if startTime, err := modelx.ParseLocalDate(strings.TrimSpace(*in.StartTime)); err == nil {
			startTimeIn = append(startTimeIn, startTime)
		}
	}
	if in.EndTime != nil && len(strings.TrimSpace(*in.EndTime)) > 0 {
		if endTime, err := modelx.ParseLocalDate(strings.TrimSpace(*in.EndTime)); err == nil {
			endTimeIn = append(endTimeIn, endTime)
		}
	}
	createdAtMap := make(map[string][]any)
	if len(startTimeIn) > 0 {
		createdAtMap["moreThanOrEqualTo"] = startTimeIn
	}
	if len(endTimeIn) > 0 {
		createdAtMap["lessThan"] = endTimeIn
	}
	filterMap[model.ProductProductColumns.ProductDate] = createdAtMap

	if isAll {
		ret, _, err := l.svcCtx.CurrentProduct.GetList(l.ctx, model.M{
			"statistic": "1",
		}, 5, filterMap)
		if err != nil {
			// return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			logx.Errorw("BackendGetProductList GetList statistic err", logx.Field("detail", err.Error()))
		} else if len(ret) > 0 {
			statusStatisticMap := make(map[uint32]uint32)
			var total int
			for _, v := range ret {
				switch int32(v.Status) {
				case globalkey.PostStatusPending:
					statusStatisticMap[2] += uint32(v.SortOrder)
				case globalkey.PostStatusPublish:
					statusStatisticMap[3] += uint32(v.SortOrder)
				case globalkey.ProductStatusUnavailable, globalkey.PostStatusFuture:
					statusStatisticMap[4] += uint32(v.SortOrder)
				case globalkey.ProductStatusPrivate:
					statusStatisticMap[5] += uint32(v.SortOrder)
				case globalkey.PostStatusRejected:
					statusStatisticMap[6] += uint32(v.SortOrder)
				case globalkey.PostStatusDraft, globalkey.PostStatusAutoDraft:
					statusStatisticMap[7] += uint32(v.SortOrder)
				case globalkey.PostStatusTrash:
					statusStatisticMap[8] += uint32(v.SortOrder)
				}
				if v.Status != uint16(globalkey.PostStatusTrash) {
					total += v.SortOrder
				}
			}
			statusStatisticMap[1] = uint32(total)
			resp.StatusStatisticMap = statusStatisticMap
		}
	}

	if !statusIsSet {
		statusIn := modelx.UniqueInterfaces(stati, 32)
		if len(statusIn) > 0 {
			statusMap["in"] = statusIn
		}
		if in.Status != nil {
			statusMap["equalTo"] = []any{*in.Status}
		}
		filterMap[model.ProductProductColumns.Status] = statusMap
	}

	var search string
	if in.Search != nil && len(strings.TrimSpace(*in.Search)) >= 2 {
		search = strings.TrimSpace(*in.Search)
	}
	var orderBy string
	if len(orderBys) > 0 {
		orderBy = strings.Join(orderBys, ",")
	}
	res, count, err := l.svcCtx.CurrentProduct.GetList(l.ctx, model.M{
		"page":     page,
		"pageSize": pageSize,
		"search":   search,
		"orderBy":  orderBy,
	}, withCount, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if restricted {
		count = preCount
	}
	resp.Total = uint64(count)

	if len(res) > 0 {
		var pids []any
		for _, v := range res {
			pids = append(pids, v.ID)
		}

		idDetailMap := make(map[uint64]*model.ProductDetail)
		if (in.WithContent != nil && *in.WithContent > 0) || (in.WithTracing != nil && *in.WithTracing > 0) {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductDetailColumns.ProductID] = map[string][]any{
				"in": pids,
			}
			list, _, err := l.svcCtx.CurrentProduct.GetDetailList(l.ctx, model.M{
				"orderBy": model.ProductDetailColumns.DetailID + " DESC",
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					if _, ok := idDetailMap[v.ProductID]; !ok {
						idDetailMap[v.ProductID] = v
					}
				}
			}
		}

		idMetumMap := make(map[uint64][]*model.ProductMetum)
		if in.WithMeta != nil && *in.WithMeta > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductMetumColumns.ProductID] = map[string][]any{
				"in": pids,
			}
			subFilterMap[model.ProductMetumColumns.MetaKey] = map[string][]any{
				"in": {
					"_thumbnail_id",
					"_thumbnail_url",
					"_photo_count",
					"_view_count",
					"_comment_count",
					"_forward_count",
					"_pending_status",
					"_sticky",
					"_gem_price_min",
					"_gem_price_max",
					"_retail_price_min",
					"_retail_price_max",
					"_stock",
					"_total_sale_count",
					"_is_hot",
					"_sync",
					"_sync_from",
					"abbr",
					// "_is_new",
					// "_rating_count",
					// "_average_rating",
				},
			}
			list, _, err := l.svcCtx.CurrentProduct.GetMeta(l.ctx, model.M{
				"orderBy": model.ProductMetumColumns.MetaID + " DESC",
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				keyMap := make(map[uint64]map[string]struct{})
				for _, v := range list {
					key := v.MetaKey
					if len(key) > 0 && v.MetaValue.Valid {
						if subMap, ok := keyMap[v.MetaID]; ok { // 防重复
							if _, ok := subMap[key]; ok {
								continue
							}

							keyMap[v.MetaID][key] = struct{}{}
						} else {
							keyMap[v.MetaID] = make(map[string]struct{})
						}

						idMetumMap[v.ProductID] = append(idMetumMap[v.ProductID], v)
					}
				}
			}
		}

		idMediaMap := make(map[uint64][]*product.MediaInfo)
		if in.WithMedia != nil && *in.WithMedia > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductAttachmentRelationshipColumns.ObjectID] = map[string][]any{
				"in": pids,
			}
			subFilterMap[model.ProductAttachmentRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.AttachmentLogTypeProduct},
			}
			subFilterMap[model.ProductAttachmentRelationshipColumns.AttachmentThumbnail] = map[string][]any{
				"equalTo": {1},
			}
			list, _, err := l.svcCtx.CurrentProduct.GetMedia(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &product.MediaInfo{
						Uid:       &v.AttachmentRelationshipID,
						Id:        pointy.GetPointer(strconv.FormatUint(v.AttachmentID, 10)),
						Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
						Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
						Uri:       &v.AttachmentSource,
						Url:       &v.AttachmentImageURL,
						AltText:   &v.AttachmentImageAlt,
						MetaData:  &v.AttachmentImageMeta,
					})
				}
			}
		}

		idCategoryMap := make(map[uint64][]*product.TermInfo)
		idTagMap := make(map[uint64][]*product.TermInfo)
		if (in.WithCategory != nil && *in.WithCategory > 0) || (in.WithTag != nil && *in.WithTag > 0) {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductTermRelationshipColumns.ObjectID] = map[string][]any{
				"in": pids,
			}
			subFilterMap[model.ProductTermRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.PostTypeProduct},
			}
			list, _, err := l.svcCtx.CurrentProductTerm.GetRelationshipList(l.ctx, model.M{
				"select": model.ProductTermRelationshipColumns.ObjectID + "," + model.ProductTermRelationshipColumns.TermID,
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				tidMap := make(map[uint64][]uint64) // Map: model.ProductTerm.TermID => []model.ProductProduct.ID
				var tids []any                      // []model.ProductTerm.TermID(已去重)
				for _, v := range list {
					if _, ok := tidMap[v.TermID]; !ok {
						tids = append(tids, v.TermID)
					}
					tidMap[v.TermID] = append(tidMap[v.TermID], v.ObjectID)
				}

				termFilterMap := make(map[string]map[string][]any)
				termFilterMap[model.ProductTermColumns.TermID] = map[string][]any{
					"in": tids,
				}
				terms, _, err := l.svcCtx.CurrentProductTerm.GetList(l.ctx, model.M{}, -1, termFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(terms) > 0 {
					for _, term := range terms {
						if oids, ok := tidMap[term.TermID]; ok {
							if term.Taxonomy == uint16(globalkey.TermTaxonomyCategory) { // 是分类
								if in.WithCategory != nil && *in.WithCategory > 0 {
									oidMap := make(map[uint64]struct{})
									for _, oid := range oids {
										if _, ok := oidMap[oid]; !ok {
											idCategoryMap[oid] = append(idCategoryMap[oid], &product.TermInfo{
												Id:   &term.TermID,
												Name: &term.Name,
												Slug: &term.Slug,
											})
											oidMap[oid] = struct{}{}
										}
									}
								}
							} else if term.Taxonomy == uint16(globalkey.TermTaxonomyProductTag) { // 是分类
								if in.WithTag != nil && *in.WithTag > 0 {
									oidMap := make(map[uint64]struct{})
									for _, oid := range oids {
										if _, ok := oidMap[oid]; !ok {
											idTagMap[oid] = append(idTagMap[oid], &product.TermInfo{
												Id:   &term.TermID,
												Name: &term.Name,
												Slug: &term.Slug,
											})
											oidMap[oid] = struct{}{}
										}
									}
								}
							}
						}
					}
				}
			}
		}

		idEntryMap := make(map[uint64][]*product.EntryInfo)
		if in.WithEntry != nil && *in.WithEntry > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductKnowledgeBaseRelationshipColumns.ProductID] = map[string][]any{
				"in": pids,
			}
			list, _, err := l.svcCtx.CurrentProduct.GetEntryList(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					if v.ToEntryID > 0 {
						idEntryMap[v.ProductID] = append(idEntryMap[v.ProductID], &product.EntryInfo{
							Id:   &v.ToEntryID,
							Key:  pointy.GetPointer(globalkey.ProductRelationNamePerfume),
							Name: &v.ToEntryName,
							Slug: &v.ToEntrySlug,
						})
					}
					if v.ToEntryGroupID > 0 {
						idEntryMap[v.ProductID] = append(idEntryMap[v.ProductID], &product.EntryInfo{
							Id:   &v.ToEntryGroupID,
							Key:  pointy.GetPointer(globalkey.ProductRelationNameBrand),
							Name: &v.ToEntryGroupName,
							Slug: &v.ToEntryGroupSlug,
						})
					}
				}
			}
		}

		idAttrMap := make(map[uint64][]*product.ProductAttrInfo)
		idSpecMap := make(map[uint64][]*product.ProductAttrInfo)
		if in.WithAttr != nil && *in.WithAttr > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductAttributeColumns.ProductID] = map[string][]any{
				"in": pids,
			}
			if in.WithSpec == nil || *in.WithSpec == 0 {
				subFilterMap[model.ProductAttributeColumns.IsVariationAttribute] = map[string][]any{
					"equalTo": {0},
				}
			}

			list, _, err := l.svcCtx.CurrentProduct.GetAttributeList(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				var attrIds []any
				for _, v := range list {
					attrIds = append(attrIds, v.ProductAttributeID)
				}

				optFilterMap := make(map[string]map[string][]any)
				optFilterMap[model.ProductAttributeOptionColumns.AttributeID] = map[string][]any{
					"in": attrIds,
				}
				optList, _, err := l.svcCtx.CurrentProduct.GetAttributeOptionList(l.ctx, model.M{}, -1, optFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				attrOptMap := make(map[uint64][]*product.ProductAttrOptInfo)
				if len(optList) > 0 {
					for _, v := range optList {
						attrOptMap[v.AttributeID] = append(attrOptMap[v.AttributeID], &product.ProductAttrOptInfo{
							Uid:      &v.OptionID,
							AttrId:   &v.AttributeID,
							Id:       &v.TaxonomyOptionID,
							Status:   pointy.GetPointer(int32(v.Status)),
							Name:     &v.Name,
							Slug:     &v.Slug,
							MediaId:  pointy.GetPointer(strconv.FormatInt(int64(v.ThumbnailID), 10)),
							MediaUrl: &v.ThumbnailURL,
						})
					}
				}

				for _, v := range list {
					var opts []*product.ProductAttrOptInfo
					if os, ok := attrOptMap[v.ProductAttributeID]; ok {
						opts = os[:]
					}

					if v.IsVariationAttribute > 0 {
						idAttrMap[v.ProductID] = append(idAttrMap[v.ProductID], &product.ProductAttrInfo{
							Uid:         &v.ProductAttributeID,
							ProductId:   &v.ProductID,
							TermId:      &v.TermID,
							Id:          &v.Taxonomy,
							IsVariation: pointy.GetPointer(uint32(v.IsVariationAttribute)),
							Name:        &v.Name,
							Slug:        &v.Slug,
							HasMedia:    pointy.GetPointer(uint32(v.HasImage)),
							Status:      pointy.GetPointer(int32(v.Status)),
							Options:     opts,
						})
					} else {
						idSpecMap[v.ProductID] = append(idSpecMap[v.ProductID], &product.ProductAttrInfo{
							Uid:         &v.ProductAttributeID,
							ProductId:   &v.ProductID,
							TermId:      &v.TermID,
							Id:          &v.Taxonomy,
							IsVariation: pointy.GetPointer(uint32(v.IsVariationAttribute)),
							Name:        &v.Name,
							Slug:        &v.Slug,
							HasMedia:    pointy.GetPointer(uint32(v.HasImage)),
							Status:      pointy.GetPointer(int32(v.Status)),
							Options:     opts,
						})
					}
				}
			}
		}

		idSkuMap := make(map[uint64][]*product.ProductVariationInfo)
		if in.WithSku != nil && *in.WithSku > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductVariationColumns.ProductID] = map[string][]any{
				"in": pids,
			}
			list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				var skuIds []any
				for _, v := range list {
					skuIds = append(skuIds, v.ProductVariationID)
				}

				skuMetaFilterMap := make(map[string]map[string][]any)
				skuMetaFilterMap[model.ProductVariationMetumColumns.ProductID] = map[string][]any{
					"in": skuIds,
				}
				skuMetaFilterMap[model.ProductVariationMetumColumns.MetaKey] = map[string][]any{
					"in": {
						"_thumbnail_id",
						"_thumbnail_url",
						"_sku_key",
						"_sku",
						"_stock",
						"_pre_sale_count", // 预定义的销量
					},
				}
				skuMetaList, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{
					"orderBy": model.ProductVariationMetumColumns.MetaID,
				}, -1, skuMetaFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				skuMetaMap := make(map[uint64]*product.ProductVariationInfo)
				if len(skuMetaList) > 0 {
					for _, v := range skuMetaList {
						key := strings.TrimSpace(v.MetaKey)
						if len(key) > 0 && v.MetaValue.Valid {
							if _, ok := skuMetaMap[v.ProductID]; !ok {
								skuMetaMap[v.ProductID] = &product.ProductVariationInfo{}
							}
							val := v.MetaValue.String
							switch key {
							case "_thumbnail_id":
								skuMetaMap[v.ProductID].MediaId = pointy.GetPointer(val)
							case "_thumbnail_url":
								skuMetaMap[v.ProductID].MediaUrl = pointy.GetPointer(val)
							case "_sku_key": // 2  属性值ID
								skuMetaMap[v.ProductID].Key = pointy.GetPointer(val)
							case "_sku": // 1-净含量-Net volume:0-10ml-  属性及其值
								skuMetaMap[v.ProductID].Value = pointy.GetPointer(val)
							case "_stock":
								if i, err := strconv.ParseInt(val, 10, 32); err == nil {
									skuMetaMap[v.ProductID].Inventory = pointy.GetPointer(uint32(i))
								}
							case "_pre_sale_count":
								if i, err := strconv.ParseInt(val, 10, 32); err == nil {
									skuMetaMap[v.ProductID].SaleCount = pointy.GetPointer(uint32(i))
								}
							}
						}
					}
				}

				for _, v := range list {
					var mediaId *string
					var mediaUrl *string
					var inventory *uint32
					var saleCount *uint32
					var specs []*product.ProductAttrInfo
					if v, ok := skuMetaMap[v.ProductVariationID]; ok {
						mediaId = v.MediaId
						mediaUrl = v.MediaUrl
						inventory = v.Inventory
						saleCount = v.SaleCount
						specs = l.parseSpecs(v.Value, v.Key)
					}
					idSkuMap[v.ProductID] = append(idSkuMap[v.ProductID], &product.ProductVariationInfo{
						Id:           &v.ProductVariationID,
						GemPrice:     pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice))),
						RetailPrice:  pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailPrice))),
						CounterPrice: pointy.GetPointer(moneyx.FormatAmount(int64(v.CounterPrice))),
						Weight:       pointy.GetPointer(moneyx.FormatWeight(int64(v.Weight))),
						SkuCode:      &v.SkuCode,
						SkuType:      pointy.GetPointer(uint32(v.SkuType)),
						Status:       pointy.GetPointer(uint32(v.Status)),
						MediaId:      mediaId,
						MediaUrl:     mediaUrl,
						Inventory:    inventory,
						SaleCount:    saleCount,
						Specs:        specs,
					})
				}
			}
		}

		for _, v := range res {
			var content *string
			var contentFiltered *string
			var tracing *string
			var tracingFiltered *string
			var meta []*product.MetaInfo
			var media []*product.MediaInfo
			var commentCount int64
			var sticky int32
			var pendingStatus int32
			if detail, ok := idDetailMap[v.ID]; ok {
				content = pointy.GetPointer(detail.Content)
				contentFiltered = pointy.GetPointer(detail.ContentFiltered)
				if detail.Tracing.Valid {
					tracing = pointy.GetPointer(detail.Tracing.String)
				}
				if detail.TracingFiltered.Valid {
					tracingFiltered = pointy.GetPointer(detail.TracingFiltered.String)
				}
			}
			if ms, ok := idMetumMap[v.ID]; ok {
				var mediaId *string
				var mediaUrl *string
				for _, m := range ms {
					key := strings.TrimSpace(m.MetaKey)
					if len(key) > 0 && m.MetaValue.Valid {
						val := m.MetaValue.String
						switch key {
						case "_thumbnail_id":
							mediaId = pointy.GetPointer(val)
						case "_thumbnail_url":
							mediaUrl = pointy.GetPointer(val)
						case "_photo_count":
							meta = append(meta, &product.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer("photoCount"),
								Value: pointy.GetPointer(val),
							})
						case "_pending_status":
							if i, err := strconv.ParseInt(val, 10, 32); err == nil {
								pendingStatus = int32(i)
								meta = append(meta, &product.MetaInfo{
									Id:    &m.MetaID,
									Key:   pointy.GetPointer("pendingStatus"),
									Value: pointy.GetPointer(val),
								})
							}
						case "_view_count":
							meta = append(meta, &product.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer("viewCount"),
								Value: pointy.GetPointer(val),
							})
						case "_comment_count":
							if i, err := strconv.ParseInt(val, 10, 32); err == nil {
								commentCount = i
							}
						case "_forward_count":
							meta = append(meta, &product.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer("forwardCount"),
								Value: pointy.GetPointer(val),
							})
						case "_sticky":
							switch val {
							case "frontpage":
								sticky = globalkey.BlogStickyTypeFrontpage
							case "category":
								sticky = globalkey.BlogStickyTypeCategory
							case "tag":
								sticky = globalkey.BlogStickyTypeTag
							}
						case "_gem_price_min":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("gemPriceMin"),
								Value: pointy.GetPointer(val),
							})
						case "_gem_price_max":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("gemPriceMax"),
								Value: pointy.GetPointer(val),
							})
						case "_retail_price_min":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("retailPriceMin"),
								Value: pointy.GetPointer(val),
							})
						case "_retail_price_max":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("retailPriceMax"),
								Value: pointy.GetPointer(val),
							})
						case "_stock":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("stock"),
								Value: pointy.GetPointer(val),
							})
						case "_total_sale_count":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("saleCount"),
								Value: pointy.GetPointer(val),
							})
						case "_is_hot":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("isHot"),
								Value: pointy.GetPointer(val),
							})
						case "_sync":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("sync"),
								Value: pointy.GetPointer(val),
							})
						case "_sync_from":
							meta = append(meta, &product.MetaInfo{
								Key:   pointy.GetPointer("syncFrom"),
								Value: pointy.GetPointer(val),
							})
						default:
							meta = append(meta, &product.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer(key),
								Value: pointy.GetPointer(val),
							})
						}
					}
				}
				if mediaUrl != nil {
					media = append(media, &product.MediaInfo{
						Id:        mediaId,
						Thumbnail: pointy.GetPointer(uint32(1)),
						Url:       mediaUrl,
					})
				}
			}
			if ms, ok := idMediaMap[v.ID]; ok {
				media = ms[:]
			}
			status := int32(v.Status)
			var visibility uint32
			if status == globalkey.PostStatusPending {
				switch pendingStatus {
				case globalkey.PostStatusPrivate:
					visibility = 3
				case globalkey.PostStatusFuture:
					visibility = 2
				default:
					visibility = 1
				}
			} else {
				switch status {
				case globalkey.PostStatusPrivate:
					visibility = 3
				case globalkey.PostStatusFuture:
					visibility = 2
				default:
					visibility = 1
				}
			}

			var categories []*product.TermInfo
			if terms, ok := idCategoryMap[v.ID]; ok {
				categories = terms[:]
			}

			var tags []*product.TermInfo
			if terms, ok := idTagMap[v.ID]; ok {
				tags = terms[:]
			}

			var entries []*product.EntryInfo
			if es, ok := idEntryMap[v.ID]; ok {
				entries = es[:]
			}

			var attrs []*product.ProductAttrInfo
			if as, ok := idAttrMap[v.ID]; ok {
				attrs = as[:]
			}
			var specs []*product.ProductAttrInfo
			if ss, ok := idSpecMap[v.ID]; ok {
				attrs = ss[:]
			}

			var skus []*product.ProductVariationInfo
			if ss, ok := idSkuMap[v.ID]; ok {
				skus = ss[:]
			}

			resp.List = append(resp.List, &product.ProductInfo{
				Id:              &v.ID,
				Title:           &v.Title,
				Slug:            &v.Slug,
				ProductCode:     &v.ProductCode,
				Content:         content,
				ContentFiltered: contentFiltered,
				Tracing:         tracing,
				TracingFiltered: tracingFiltered,
				Excerpt:         &v.Excerpt,
				Status:          pointy.GetPointer(uint32(status)),
				Visibility:      pointy.GetPointer(uint32(visibility)), // Todo: 重构 临时用法：借用visibility传递publicTime的值
				Sort:            pointy.GetPointer(uint32(v.SortOrder)),
				Uri:             &v.GUID,
				AuthorId:        &v.ProductAuthorID,
				CategoryId:      &v.CategoryID,
				ParentId:        &v.ParentID,
				Sticky:          pointy.GetPointer(uint32(sticky)),
				VirtualType:     pointy.GetPointer(uint32(v.VirtualType)),
				CreatedAt:       pointy.GetPointer(modelx.FormatTime(v.ProductDate, "")),
				UpdatedAt:       pointy.GetPointer(modelx.FormatTime(v.ProductModifiedDate, "")),
				DeletedAt:       pointy.GetPointer(modelx.FormatNullDotTime(v.DeletedAt, "")),
				CommentCount:    pointy.GetPointer(uint32(commentCount)),
				Meta:            meta,
				Media:           media,
				Categories:      categories,
				Tags:            tags,
				Relations:       entries,
				Attrs:           attrs,
				Specs:           specs,
				Skus:            skus,
			})
		}
	}

	return resp, nil
}

func (l *BackendGetProductListLogic) preFilterByVariation(in *product.ProductListReq) (res []any, err error) {
	if vals := modelx.UniqueInterfaces(*in.SkuIds, 64); len(vals) == 0 {
		return
	} else {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductVariationColumns.ProductVariationID] = map[string][]any{
			"in": vals,
		}
		list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			for _, v := range list {
				if v.ProductID == 0 {
					continue
				}

				res = append(res, v.ProductID)
			}
		}
	}

	return
}

func (l *BackendGetProductListLogic) preFilterByBrandRelation(in *product.ProductListReq) (res []any, err error) {
	if vals := modelx.UniqueInterfaces(*in.BrandIds, 64); len(vals) == 0 {
		return
	} else {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductKnowledgeBaseRelationshipColumns.ToEntryGroupID] = map[string][]any{
			"in": vals,
		}
		list, _, err := l.svcCtx.CurrentProduct.GetEntryList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			for _, v := range list {
				if v.ProductID == 0 {
					continue
				}

				res = append(res, v.ProductID)
			}
		}
	}

	return
}

func (l *BackendGetProductListLogic) preFilterByPerfumeRelation(in *product.ProductListReq) (res []any, err error) {
	if vals := modelx.UniqueInterfaces(*in.PerfumeIds, 64); len(vals) == 0 {
		return
	} else {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductKnowledgeBaseRelationshipColumns.ToEntryID] = map[string][]any{
			"in": vals,
		}
		list, _, err := l.svcCtx.CurrentProduct.GetEntryList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			for _, v := range list {
				if v.ProductID == 0 {
					continue
				}

				res = append(res, v.ProductID)
			}
		}
	}

	return
}

func (l *BackendGetProductListLogic) preFilterByTermRelation(in *product.ProductListReq) (res []any, err error) {
	if vals := modelx.UniqueInterfaces(*in.CategoryIds, 64); len(vals) == 0 {
		return
	} else {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductTermRelationshipColumns.TermID] = map[string][]any{
			"in": vals,
		}
		list, _, err := l.svcCtx.CurrentProduct.GetRelationshipList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			for _, v := range list {
				if v.ObjectID == 0 {
					continue
				}

				res = append(res, v.ObjectID)
			}
		}
	}

	return
}

func (l *BackendGetProductListLogic) parseSpecs(sku *string, skuKey *string) (res []*product.ProductAttrInfo) {
	if sku == nil || skuKey == nil {
		return
	}

	uids := strings.Split(*skuKey, arrayValueSeparator) // 2,10
	if len(uids) > 0 {
		opts := strings.Split(*sku, arrayKeyValueSeparator) // 1-净含量-Net volume:0-10ml-;
		if len(opts) > 0 && len(opts) == len(uids) {
			for idx, opt := range opts {
				parts := strings.Split(opt, keyValueSeparator)
				if len(parts) == 2 {
					if uid, err := strconv.ParseInt(uids[idx], 10, 64); err == nil {
						specParts := strings.Split(parts[0], LSVSeparator)
						optParts := strings.Split(parts[1], LSVSeparator)
						if len(specParts) == 3 && len(optParts) == 3 {
							if specId, err := strconv.ParseInt(specParts[0], 10, 64); err == nil {
								if optId, err := strconv.ParseInt(optParts[0], 10, 64); err == nil {
									options := []*product.ProductAttrOptInfo{{
										Uid:   pointy.GetPointer(uint64(uid)),
										Id:    pointy.GetPointer(uint64(optId)),
										Label: pointy.GetPointer(optParts[1]),
										Slug:  pointy.GetPointer(optParts[2]),
									}}
									res = append(res, &product.ProductAttrInfo{
										Id:      pointy.GetPointer(uint64(specId)),
										Label:   pointy.GetPointer(specParts[1]),
										Slug:    pointy.GetPointer(specParts[2]),
										Options: options,
									})
								}
							}
						}
					}
				}
			}
		}
	}

	return
}

func (l *BackendGetProductListLogic) backendGetProductListStock(in *product.ProductListReq) (*product.ProductListResp, error) {
	var total uint64

	filterMap := make(map[string]map[string][]any)
	filterMap[model.ProductVariationMetumColumns.MetaKey] = map[string][]any{
		"in": []any{"_stock"},
	}

	list, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{
		"page":     in.Page,
		"pageSize": in.PageSize,
		"orderBy":  model.ProductVariationMetumColumns.MetaID,
	}, -1, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if len(list) > 0 {
		for _, item := range list {
			key := strings.TrimSpace(item.MetaKey)
			if len(key) > 0 && item.MetaValue.Valid {
				val := strings.TrimSpace(item.MetaValue.String)

				if key == "_stock" && item.ProductID > 0 {
					if i, err := strconv.ParseInt(val, 10, 32); err == nil {
						stockKey := l.getStockKey(item.ProductID)
						_, err = l.svcCtx.BizRedis.SetnxCtx(l.ctx, stockKey, strconv.FormatInt(i, 10))
						if err != nil {
							logrus.Info(fmt.Sprintf("diagnoseStock SetnxCtx err: %+v", err))
						}

						stockFrozenKey := l.getStockFrozenKey(item.ProductID)
						_, err = l.svcCtx.BizRedis.SetnxCtx(l.ctx, stockFrozenKey, "0")
						if err != nil {
							logrus.Info(fmt.Sprintf("diagnoseStock SetnxCtx frozen err: %+v", err))
						}
					}
				}
			}

			total += 1
		}
	}

	return &product.ProductListResp{
		Total: total,
	}, nil
}

func (l *BackendGetProductListLogic) getStockKey(vid uint64) string {
	return fmt.Sprintf("{%s}:%d", cachex.SkuStock, vid)
}

func (l *BackendGetProductListLogic) getStockFrozenKey(vid uint64) string {
	return fmt.Sprintf("{%s}:%d:%s", cachex.SkuStock, vid, cachex.SkuStockFrozen)
}
